package org.kca.yarmour.utils;

import java.awt.FontMetrics;
import java.awt.Image;
import java.awt.Toolkit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.kca.graphics.FontPalette;

public class ImageUtils
{

    /**
     * Loads an image from a file
     * @param filename The filename to load from
     * @return The image object representing that image
     */
    public static Image loadImage(String filename){
        return Toolkit.getDefaultToolkit().createImage(filename);
    }

    /**
     * Calculates the left-x position for the font to be drawn, in order for the
     * string to be right aligned at a given position
     * @param fp the Font Palette used to render the text
     * @param rightX the right aligned x position
     * @param text the text to align
     * @return the left aligned x position to draw the text
     */
    public static int rightAlignX(FontPalette fp, int rightX, String text)
    {
        int w = fp.stringWidth(text);
        return rightX - w;
    }
    
    /**
     * Calculates the left-x position for the font to be drawn, in order for the
     * string to be center aligned between two points
     * @param fp the Font Palette used to render the text
     * @param leftX the left x position
     * @param rightX the right x position
     * @param text the text to align
     * @return the left aligned x position to draw the text
     */
    public static int centerAlignX(FontPalette fp, int leftX, int rightX, String text)
    {
        return centerAlignX(fp, (leftX + rightX) / 2, text);
    }
    
    /**
     * Calculates the left-x position for the font to be drawn, in order for the
     * string to be center aligned at a specified x coordinate
     * @param fp the Font Palette used to render the text
     * @param centerX the center x coordinate
     * @param text the text to align
     * @return the left aligned x position to draw the text
     */
    public static int centerAlignX(FontPalette fp, int centerX, String text)
    {
        int w = fp.stringWidth(text);
        return centerX - w / 2;
    }
    
    /**
     * Split a string into words
     * @param s the string to split
     * @param splitDashes set to true if dashes are to be considered a word
     * boundary, false if dashes are to be considered a word character
     * @return the split string
     */
    public static String[] splitWords(String s, boolean splitDashes)
    {
        ArrayList<String> list = new ArrayList<String>();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < s.length() - 1; i++)
        {
            char q = s.charAt(i);
            char r = s.charAt(i + 1);
            if (isWordCharacter(q, splitDashes))
            {
                sb.append(q);
                if (!isWordCharacter(r, splitDashes))
                {
                    sb.append(r);
                    list.add(sb.toString());
                    sb.delete(0, sb.length());
                    i++;
                }
            }
        }
        char p = s.charAt(s.length() - 1);
        if (isWordCharacter(p, splitDashes))
        {
            sb.append(s.charAt(s.length() - 1));
        }
        if (sb.length() > 0)
        {
            list.add(sb.toString());
        }
        return list.toArray(new String[list.size()]);
    }

    private static boolean isWordCharacter(char q, boolean flag)
    {
        if (Character.isLetterOrDigit(q) || q == '\'')
        {
            return true;
        }
        if (!flag)
        {
            if (q == '-' || q == '_')
            {
                return true;
            }
        }

        return false;
    }

    /**
     * Split a string into a preferred number of lines based on size on the screen
     * @param s the string to split
     * @param lines the preferred number of lines
     * @param font the Font used
     * @param splitDashes set to true to split dashes, false to preserve
     * words with dashes
     */
    public static String[] splitString(String s, int lines, FontPalette font, boolean splitDashes)
    {
        String[] words = splitWords(s, splitDashes);
        if (words.length <= lines)
        {
            for (int i = 0; i < words.length; i++)
            {
                words[i] = words[i].trim();
            }
            return words;
        }
        double[] sizes = new double[words.length];
        for (int i = 0; i < words.length; i++)
        {
            sizes[i] = font.stringWidth(words[i]);
        }
        double[] cumulative = new double[words.length + 1];
        cumulative[0] = 0;
        for (int i = 1; i < cumulative.length; i++)
        {
            cumulative[i] = cumulative[i - 1] + sizes[i - 1];
        }
        double[][] dp = new double[words.length][lines + 1];
        for (int i = 0; i < words.length; i++)
        {
            Arrays.fill(dp[i], 0);
        }
        double minlen = solve(dp, cumulative, 0, lines);
        // get the answer
        String[] ans = new String[lines];
        StringBuffer sb = new StringBuffer();
        double count = 0;
        int index = 0;
        for (int i = 0; i < sizes.length; i++)
        {
            count += sizes[i];
            if (count > minlen + 0.1)
            {
                ans[index++] = sb.toString().trim();
                sb.delete(0, sb.length());
                sb.append(words[i]);
                count = sizes[i];
            }
            else
            {
                sb.append(words[i]);
            }
        }
        if (index != lines)
        {
            ans[index] = sb.toString().trim();
        }
        return ans;
    }

    /**
     * Splits a string into lines so that they will fit a certain width 
     * @param s the string to split
     * @param width the width to fit into
     * @param font the font
     * @param bywords set to true if the string is to be wrapped by word boundaries, 
     * otherwise it will be wrapped at character boundaries
     * @return the split string
     */
    public static List<String> wrapString(String s, int width, FontPalette font, boolean byWords)
    {
        List<String> list = new ArrayList<String>();
        int spacelen = font.stringWidth(" ");
        if (byWords)
        {
            String[] words = s.split(" ");
            int ptr = 0;
            int base = 0;
            int sum = 0;
            while (ptr < words.length)
            {
                int w = font.stringWidth(words[ptr]);
                if (w + spacelen + sum > width)
                {
                    StringBuffer sb = new StringBuffer();
                    for (int i = base; i < ptr; i++)
                    {
                        sb.append(words[i]);
                        if (i != ptr - 1) 
                        {
                            sb.append(" ");       
                        }
                    }
                    list.add(sb.toString());
                    base = ptr;
                    sum = w;
                }
                else
                {
                    sum += w + spacelen;
                }
                ptr++;
            }
            if (base < words.length)
            {
                StringBuffer sb = new StringBuffer();
                for (int i = base; i < words.length; i++)
                {
                    sb.append(words[i]);
                    if (i != ptr - 1) 
                    {
                        sb.append(" ");       
                    }
                }
                list.add(sb.toString());
            }
        }
        else
        {
            int ptr = 0;
            int base = 0;
            int sum = 0;
            while (ptr < s.length())
            {
                int w = font.charWidth(s.charAt(ptr));
                if (w + sum > width)
                {
                    StringBuffer sb = new StringBuffer();
                    for (int i = base; i < ptr; i++)
                    {
                        sb.append(s.charAt(i));
                    }
                    list.add(sb.toString());
                    base = ptr;
                    sum = w;
                }
                else
                {
                    sum += w;
                }
                ptr++;
            }
            if (base < s.length())
            {
                StringBuffer sb = new StringBuffer();
                for (int i = base; i < s.length(); i++)
                {
                    sb.append(s.charAt(i));
                }
                list.add(sb.toString());
            }
        }
        return list;
    }

    private static double solve(double[][] dp, double[] cl, int pos, int left)
    {
        if (dp[pos][left] != 0)
        {
            return dp[pos][left];
        }
        if (left == 1)
        {
            dp[pos][1] = cl[cl.length - 1] - cl[pos];
            return dp[pos][1];
        }
        double bestLen = 99999;
        for (int i = pos + 1; i < cl.length - 1; i++)
        {
            double div = solve(dp, cl, i, left - 1);
            double self = cl[i] - cl[pos];
            double m = Math.max(div, self);
            if (m < bestLen)
            {
                bestLen = m;
            }
        }
        dp[pos][left] = bestLen;
        return bestLen;
    }
}
